Add a simple validation demo. Make it possible to add arbitrary children
authorMatthias Clasen <mclasen@redhat.com>
Thu, 26 Apr 2007 15:55:48 +0000 (15:55 +0000)
committerMatthias Clasen <matthiasc@src.gnome.org>
Thu, 26 Apr 2007 15:55:48 +0000 (15:55 +0000)
2007-04-26  Matthias Clasen  <mclasen@redhat.com>

        * demo/gtk-demo/combobox.c: Add a simple validation demo.
        * gtk/gtkcomboboxentry.c: Make it possible to add arbitrary
        children to a GtkComboBoxEntry.  (#426401, Paul Pogonyshev)

svn path=/trunk/; revision=17657

ChangeLog
demos/gtk-demo/combobox.c
docs/reference/ChangeLog
docs/reference/gtk/tmpl/gtkcomboboxentry.sgml
gtk/gtkcomboboxentry.c

index 2e9da6e8a2a777455c5c7c3ceac85ac3e0b47760..384e05adc3f95ef64e8b55d23bdc605b5988ea19 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2007-04-26  Matthias Clasen  <mclasen@redhat.com>
+
+       * demo/gtk-demo/combobox.c: Add a simple validation demo. 
+       * gtk/gtkcomboboxentry.c: Make it possible to add arbitrary
+       children to a GtkComboBoxEntry.  (#426401, Paul Pogonyshev)
+
 2007-04-26  Tor Lillqvist  <tml@novell.com>
 
        * gtk/gtkinputdialog.c (gtk_input_dialog_fill_axes)
index 99d9681584420ce6163b9a9f15e5dd8858d9c6a9..7b47032bfcf954d5b08165d8a56be3be6071cfeb 100644 (file)
@@ -246,11 +246,87 @@ fill_combo_entry (GtkWidget *entry)
   gtk_combo_box_append_text (GTK_COMBO_BOX (entry), "Three");
 }
 
+
+/* A simple validating entry */
+
+#define TYPE_MASK_ENTRY             (mask_entry_get_type ())
+#define MASK_ENTRY(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_MASK_ENTRY, MaskEntry))
+#define MASK_ENTRY_CLASS(vtable)    (G_TYPE_CHECK_CLASS_CAST ((vtable), TYPE_MASK_ENTRY, MaskEntryClass))
+#define IS_MASK_ENTRY(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_MASK_ENTRY))
+#define IS_MASK_ENTRY_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), TYPE_MASK_ENTRY))
+#define MASK_ENTRY_GET_CLASS(inst)  (G_TYPE_INSTANCE_GET_CLASS ((inst), TYPE_MASK_ENTRY, MaskEntryClass))
+
+
+typedef struct _MaskEntry MaskEntry;
+struct _MaskEntry
+{
+  GtkEntry entry;
+  gchar *mask;
+};
+
+typedef struct _MaskEntryClass MaskEntryClass;
+struct _MaskEntryClass
+{
+  GtkEntryClass parent_class;
+};
+
+
+static void mask_entry_editable_init (GtkEditableClass *iface);
+
+G_DEFINE_TYPE_WITH_CODE (MaskEntry, mask_entry, GTK_TYPE_ENTRY,
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE,
+                                               mask_entry_editable_init));
+
+
+static void
+mask_entry_set_background (MaskEntry *entry)
+{
+  static const GdkColor error_color = { 0, 65535, 60000, 60000 };
+
+  if (entry->mask)
+    {
+      if (!g_regex_match_simple (entry->mask, gtk_entry_get_text (GTK_ENTRY (entry)), 0, 0))
+       {
+         gtk_widget_modify_base (GTK_WIDGET (entry), GTK_STATE_NORMAL, &error_color);
+         return;
+       }
+    }
+
+  gtk_widget_modify_base (GTK_WIDGET (entry), GTK_STATE_NORMAL, NULL);
+}
+
+
+static void
+mask_entry_changed (GtkEditable *editable)
+{
+  mask_entry_set_background (MASK_ENTRY (editable));
+}
+
+
+static void
+mask_entry_init (MaskEntry *entry)
+{
+  entry->mask = NULL;
+}
+
+
+static void
+mask_entry_class_init (MaskEntryClass *klass)
+{ }
+
+
+static void
+mask_entry_editable_init (GtkEditableClass *iface)
+{
+  iface->changed = mask_entry_changed;
+}
+
+
 GtkWidget *
 do_combobox (GtkWidget *do_widget)
 {
   static GtkWidget *window = NULL;
-  GtkWidget *vbox, *frame, *box, *combo;
+  GtkWidget *vbox, *frame, *box, *combo, *entry;
   GtkTreeModel *model;
   GtkCellRenderer *renderer;
   GtkTreePath *path;
@@ -343,7 +419,7 @@ do_combobox (GtkWidget *do_widget)
     gtk_tree_path_free (path);
     gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
 
-    /* A GtkComboBoxEntry 
+    /* A GtkComboBoxEntry with validation.
      */
     frame = gtk_frame_new ("Editable");
     gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
@@ -356,6 +432,12 @@ do_combobox (GtkWidget *do_widget)
     fill_combo_entry (combo);
     gtk_container_add (GTK_CONTAINER (box), combo);
     
+    entry = g_object_new (TYPE_MASK_ENTRY, NULL);
+    MASK_ENTRY (entry)->mask = "^([0-9]*|One|Two|2\302\275|Three)$";
+     
+    gtk_container_remove (GTK_CONTAINER (combo), GTK_BIN (combo)->child);
+    gtk_container_add (GTK_CONTAINER (combo), entry);
+  
   }
 
   if (!GTK_WIDGET_VISIBLE (window))
index 13d6107d4edad44a51f0942842d52764906ad9eb..f4bbd1553d8aedefc84f9bf7f881d1394917521e 100644 (file)
@@ -1,3 +1,8 @@
+2007-04-26  Matthias Clasen  <mclasen@redhat.com>
+
+       * gtk/tmpl/gtkcomboboxentry.sgml: Mention that the entry
+       can be replaced.
+
 2007-04-25  Matthias Clasen  <mclasen@redhat.com>
 
        * gtk/tmpl/gtkdialog.sgml: Fix a wrong cross reference.
index 46bba4ff0b5034c9e6dc84262e30c3b662e0d9bf..a5d364649495202f17f781ba15ebf7be0b1ac238 100644 (file)
@@ -15,25 +15,34 @@ allow modifying it.
 <para>
 In contrast to a #GtkComboBox, the underlying model of a #GtkComboBoxEntry 
 must always have a text column (see gtk_combo_box_entry_set_text_column()),
-and the entry will show the content of the text column in the selected row.  To
-get the text from the entry, use gtk_combo_box_get_active_text().
+and the entry will show the content of the text column in the selected row.  
+To get the text from the entry, use gtk_combo_box_get_active_text().
 </para>
 
-<para>The changed signal will be emitted while typing into a GtkComboBoxEntry, 
+<para>
+The changed signal will be emitted while typing into a GtkComboBoxEntry, 
 as well as when selecting an item from the GtkComboBoxEntry's list. Use 
 gtk_combo_box_get_active() or gtk_combo_box_get_active_iter() to discover 
 whether an item was actually selected from the list.
 </para>
 
-<para>Connect to the activate signal of the GtkEntry (use gtk_bin_get_child()) to 
-detect when the user actually finishes entering text.</para>
+<para>
+Connect to the activate signal of the GtkEntry (use gtk_bin_get_child()) 
+to detect when the user actually finishes entering text.
+</para>
 
 <para>
-The convenience API to construct simple text-only #GtkComboBox<!-- -->es can
-also be used with #GtkComboBoxEntry<!-- -->s which have been constructed
+The convenience API to construct simple text-only #GtkComboBox<!-- -->es 
+can also be used with #GtkComboBoxEntry<!-- -->s which have been constructed
 with gtk_combo_box_entry_new_text().
 </para>
 
+<para>
+If you have special needs that go beyond a simple entry (e.g. input validation),
+it is possible to replace the child entry by a different widget using
+gtk_container_remove() and gtk_container_add().
+</para>
+
 <!-- ##### SECTION See_Also ##### -->
 <para>
 #GtkComboBox
index e4fc737dca1bb7c3ce221dd0839b22dd1edfe693..f8b698a849e6bc93640dc0f570db654c698aba51 100644 (file)
@@ -32,8 +32,6 @@
 
 struct _GtkComboBoxEntryPrivate
 {
-  GtkWidget *entry;
-
   GtkCellRenderer *text_renderer;
   gint text_column;
 };
@@ -46,6 +44,10 @@ static void gtk_combo_box_entry_get_property     (GObject               *object,
                                                   guint                  prop_id,
                                                   GValue                *value,
                                                   GParamSpec            *pspec);
+static void gtk_combo_box_entry_add              (GtkContainer          *container,
+                                                 GtkWidget             *child);
+static void gtk_combo_box_entry_remove           (GtkContainer          *container,
+                                                 GtkWidget             *child);
 
 static gchar *gtk_combo_box_entry_get_active_text (GtkComboBox *combo_box);
 static void gtk_combo_box_entry_active_changed   (GtkComboBox           *combo_box,
@@ -72,6 +74,7 @@ gtk_combo_box_entry_class_init (GtkComboBoxEntryClass *klass)
 {
   GObjectClass *object_class;
   GtkWidgetClass *widget_class;
+  GtkContainerClass *container_class;
   GtkComboBoxClass *combo_class;
   
   object_class = (GObjectClass *)klass;
@@ -82,6 +85,10 @@ gtk_combo_box_entry_class_init (GtkComboBoxEntryClass *klass)
   widget_class->mnemonic_activate = gtk_combo_box_entry_mnemonic_activate;
   widget_class->grab_focus = gtk_combo_box_entry_grab_focus;
 
+  container_class = (GtkContainerClass *)klass;
+  container_class->add = gtk_combo_box_entry_add;
+  container_class->remove = gtk_combo_box_entry_remove;
+
   combo_class = (GtkComboBoxClass *)klass;
   combo_class->get_active_text = gtk_combo_box_entry_get_active_text;
   
@@ -102,15 +109,14 @@ gtk_combo_box_entry_class_init (GtkComboBoxEntryClass *klass)
 static void
 gtk_combo_box_entry_init (GtkComboBoxEntry *entry_box)
 {
+  GtkWidget *entry;
+
   entry_box->priv = GTK_COMBO_BOX_ENTRY_GET_PRIVATE (entry_box);
   entry_box->priv->text_column = -1;
 
-  entry_box->priv->entry = gtk_entry_new ();
-  /* this flag is a hack to tell the entry to fill its allocation.
-   */
-  GTK_ENTRY (entry_box->priv->entry)->is_cell_renderer = TRUE;
-  gtk_container_add (GTK_CONTAINER (entry_box), entry_box->priv->entry);
-  gtk_widget_show (entry_box->priv->entry);
+  entry = gtk_entry_new ();
+  gtk_widget_show (entry);
+  gtk_container_add (GTK_CONTAINER (entry_box), entry);
 
   entry_box->priv->text_renderer = gtk_cell_renderer_text_new ();
   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (entry_box),
@@ -118,12 +124,8 @@ gtk_combo_box_entry_init (GtkComboBoxEntry *entry_box)
 
   gtk_combo_box_set_active (GTK_COMBO_BOX (entry_box), -1);
 
-  g_signal_connect (entry_box->priv->entry, "changed",
-                    G_CALLBACK (gtk_combo_box_entry_contents_changed),
-                    entry_box);
   g_signal_connect (entry_box, "changed",
                     G_CALLBACK (gtk_combo_box_entry_active_changed), NULL);
-  has_frame_changed (entry_box, NULL, NULL);
   g_signal_connect (entry_box, "notify::has-frame", G_CALLBACK (has_frame_changed), NULL);
 }
 
@@ -167,6 +169,47 @@ gtk_combo_box_entry_get_property (GObject    *object,
     }
 }
 
+static void
+gtk_combo_box_entry_add (GtkContainer *container,
+                        GtkWidget    *child)
+{
+  GtkComboBoxEntry *entry_box = GTK_COMBO_BOX_ENTRY (container);
+
+  if (!GTK_IS_ENTRY (child))
+    {
+      g_warning ("Attempting to add a widget with type %s to a GtkComboBoxEntry "
+                "(need an instance of GtkEntry or of a subclass)",
+                 G_OBJECT_TYPE_NAME (child));
+      return;
+    }
+
+  GTK_CONTAINER_CLASS (gtk_combo_box_entry_parent_class)->add (container, child);
+
+  /* this flag is a hack to tell the entry to fill its allocation.
+   */
+  GTK_ENTRY (child)->is_cell_renderer = TRUE;
+
+  g_signal_connect (child, "changed",
+                   G_CALLBACK (gtk_combo_box_entry_contents_changed),
+                   entry_box);
+  has_frame_changed (entry_box, NULL, NULL);
+}
+
+static void
+gtk_combo_box_entry_remove (GtkContainer *container,
+                           GtkWidget    *child)
+{
+  if (child && child == GTK_BIN (container)->child)
+    {
+      g_signal_handlers_disconnect_by_func (child,
+                                           gtk_combo_box_entry_contents_changed,
+                                           container);
+      GTK_ENTRY (child)->is_cell_renderer = FALSE;
+    }
+
+  GTK_CONTAINER_CLASS (gtk_combo_box_entry_parent_class)->remove (container, child);
+}
+
 static void
 gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
                                     gpointer     user_data)
@@ -178,21 +221,26 @@ gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
 
   if (gtk_combo_box_get_active_iter (combo_box, &iter))
     {
-      g_signal_handlers_block_by_func (entry_box->priv->entry,
-                                      gtk_combo_box_entry_contents_changed,
-                                      combo_box);
-
-      model = gtk_combo_box_get_model (combo_box);
-
-      gtk_tree_model_get (model, &iter, 
-                         entry_box->priv->text_column, &str, 
-                         -1);
-      gtk_entry_set_text (GTK_ENTRY (entry_box->priv->entry), str);
-      g_free (str);
-
-      g_signal_handlers_unblock_by_func (entry_box->priv->entry,
-                                        gtk_combo_box_entry_contents_changed,
-                                        combo_box);
+      GtkEntry *entry = GTK_ENTRY (GTK_BIN (combo_box)->child);
+
+      if (entry)
+       {
+         g_signal_handlers_block_by_func (entry,
+                                          gtk_combo_box_entry_contents_changed,
+                                          combo_box);
+
+         model = gtk_combo_box_get_model (combo_box);
+
+         gtk_tree_model_get (model, &iter, 
+                             entry_box->priv->text_column, &str, 
+                             -1);
+         gtk_entry_set_text (entry, str);
+         g_free (str);
+
+         g_signal_handlers_unblock_by_func (entry,
+                                            gtk_combo_box_entry_contents_changed,
+                                            combo_box);
+       }
     }
 }
 
@@ -201,11 +249,14 @@ has_frame_changed (GtkComboBoxEntry *entry_box,
                   GParamSpec       *pspec,
                   gpointer          data)
 {
-  gboolean has_frame;
+  if (GTK_BIN (entry_box)->child)
+    {
+      gboolean has_frame;
   
-  g_object_get (entry_box, "has-frame", &has_frame, NULL);
+      g_object_get (entry_box, "has-frame", &has_frame, NULL);
 
-  gtk_entry_set_has_frame (GTK_ENTRY (entry_box->priv->entry), has_frame);
+      gtk_entry_set_has_frame (GTK_ENTRY (GTK_BIN (entry_box)->child), has_frame);
+    }
 }
 
 static void
@@ -323,9 +374,10 @@ static gboolean
 gtk_combo_box_entry_mnemonic_activate (GtkWidget *widget,
                                       gboolean   group_cycling)
 {
-  GtkComboBoxEntry *entry_box = GTK_COMBO_BOX_ENTRY (widget);
+  GtkBin *entry_box = GTK_BIN (widget);
 
-  gtk_widget_grab_focus (entry_box->priv->entry);
+  if (entry_box->child)
+    gtk_widget_grab_focus (entry_box->child);
 
   return TRUE;
 }
@@ -333,9 +385,10 @@ gtk_combo_box_entry_mnemonic_activate (GtkWidget *widget,
 static void
 gtk_combo_box_entry_grab_focus (GtkWidget *widget)
 {
-  GtkComboBoxEntry *entry_box = GTK_COMBO_BOX_ENTRY (widget);
+  GtkBin *entry_box = GTK_BIN (widget);
 
-  gtk_widget_grab_focus (entry_box->priv->entry);
+  if (entry_box->child)
+    gtk_widget_grab_focus (entry_box->child);
 }
 
 
@@ -372,10 +425,10 @@ gtk_combo_box_entry_new_text (void)
 static gchar *
 gtk_combo_box_entry_get_active_text (GtkComboBox *combo_box)
 {
-  GtkComboBoxEntry *combo = GTK_COMBO_BOX_ENTRY (combo_box);
+  GtkBin *combo = GTK_BIN (combo_box);
 
-  if (combo->priv->entry)
-    return g_strdup (gtk_entry_get_text (GTK_ENTRY (combo->priv->entry)));
+  if (combo->child)
+    return g_strdup (gtk_entry_get_text (GTK_ENTRY (combo->child)));
 
   return NULL;
 }